package com.dynamo.transport.network.practise.channel.socket.nio;

import com.dynamo.transport.network.practise.channel.Channel;
import com.dynamo.transport.network.practise.channel.ChannelFuture;
import com.dynamo.transport.network.practise.channel.Channels;
import com.dynamo.transport.network.practise.channel.socket.AbstractSocketChannel;
import org.apache.commons.collections.CollectionUtils;

import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.util.Iterator;
import java.util.Set;

/**
 * @author duanyimiao
 * @create 2018-12-18 10:34 AM
 * @description
 **/
public class NioWorker extends AbstractNioSelector implements Worker {
    @Override
    public Runnable createTask(Channel channel, ChannelFuture future) {
        return new RegisterWorkerTask(channel, future);
    }

    @Override
    public void process(Set<SelectionKey> keys) {

        System.out.println("work" + keys.size());
        if (CollectionUtils.isEmpty(keys)) {
            return;
        }

        for (Iterator<SelectionKey> it = keys.iterator(); it.hasNext(); ) {
            try {
                SelectionKey key = it.next();
                it.remove();
                System.out.println("key=" + key.readyOps() + " isRead=" + key.isReadable() + " isWrite=" + key.isWritable());

                AbstractSocketChannel socketChannel = (NioAcceptSocketChannel) key.attachment();

                //TODO 该类是server和client公用的，但是client需要对connect事件感兴趣，这应该如何做？给客户端重新新建一个类？

                if (key.isConnectable()) {
                    System.out.println("has connected");
                    Channels.fireChannelConnected(socketChannel.getPipeline(), socketChannel);
                }
                //读就绪
                if (key.isReadable()) {
                    //将channel可读数据放在ByteBuffer中，然后将其转换成ChannelBuffer
                    // 最后通过Channels.fireMessageReceived方法封装好MessageEvent事件传到ChannelPipeline中的ChannelHandler进行处理消息 (熟悉的编码handler就是将ChannelBuffer转换成业务使用的对象)
                    //TODO 使用ChannelBufferFactory进行创建新的buffer对象
                    ByteBuffer buffer = ByteBuffer.allocate(8042);

                    int readed = socketChannel.channel.read(buffer);
                    //客户端已经关闭了，则关闭channel
                   if(readed <= 0){
                       socketChannel.channel.close();
                   }
                    //需要将其ByteBuffer转换成netty的 ChannelBuffer

                    //发送读取的消息到UpstreamHandler进行处理
                    Channels.fireMessageReceived(socketChannel.getPipeline(), socketChannel, buffer);

                }
                //写就绪
                if (key.isWritable()) {

                    //为了不阻塞往Channel写的业务线程，所以若通道写没有就绪那么就将MessageEvent放入writeBufferQueue中，然后在此方法中从队列中取出，最后将ChannelBuffer转换成ByteBuffer

                    //TODO 为什么当接受客户端连接后监听读和写事件，发现写事件一直就绪, 因为当前socket发送缓冲区一直都是可以发送数据状态，那Netty是如何处理的呢？
                    // 一开始只对读事件感兴趣，当往channel写如数据时，此时往writeBufferQueue写，然后对写事件感兴趣，在此方法中从queue中取出写到channel中然后去掉写事件,否则key.isWritable()一直返回true

                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private class RegisterWorkerTask implements Runnable {

        private Channel channel;
        private ChannelFuture future;

        public RegisterWorkerTask(Channel channel, ChannelFuture future) {
            this.channel = channel;
            this.future = future;
        }

        @Override
        public void run() {
            try {
                //如果该channel为Client则需要对connect刚兴趣
                if(channel instanceof NioClientSocketChannel){
                    ((AbstractSocketChannel) channel).channel.register(selector, SelectionKey.OP_CONNECT, channel);
                }

                //将客户端请求监听读和写事件
                //TODO 需要将SocketChannel放在Netty的Channel的父类中，而不是具体的Channel中
                ((AbstractSocketChannel) channel).channel.register(selector, SelectionKey.OP_READ, channel);
            } catch (Exception e) {
                e.printStackTrace();
            }


        }
    }
}
